using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.Script.Serialization;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rhino.Mocks;

namespace JsonValueProviderFactorySpike
{
    [TestClass]
    public class JsonValueProviderFactoryTests
    {
        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException), "The parameter conversion from type 'System.Int32' to type 'System.Decimal' failed because no type converter can convert between these types.")]
        public void Throws_an_exception_when_ints_are_in_json_but_destination_type_is_something_else()
        {
            // Arrange
            var student = new Student
                {
                    First = "FirstName",
                    Last = "LastName",
                    Scores = new[] {70.5M, 80.5M, 90M}
                };

            var json = new JavaScriptSerializer().Serialize(student);

            var controllerContext = MockRepository.GenerateMock<ControllerContext>();
            controllerContext.Stub(x => x.HttpContext).Return(MockRepository.GenerateMock<HttpContextBase>());
            controllerContext.HttpContext.Stub(x => x.Request).Return(MockRepository.GenerateMock<HttpRequestBase>());
            controllerContext.HttpContext.Request.Stub(x => x.ContentType).Return("application/json");
            controllerContext.HttpContext.Request.Stub(x => x.InputStream).Return(new MemoryStream(Encoding.ASCII.GetBytes(json)));

            // Act
            var valueProvider = new JsonValueProviderFactory().GetValueProvider(controllerContext);

            // Assert
            Assert.AreEqual("{\"First\":\"FirstName\",\"Last\":\"LastName\",\"Scores\":[70.5,80.5,90]}", json);
            Assert.AreEqual("FirstName", (string)valueProvider.GetValue("First").ConvertTo(typeof(string)));
            Assert.AreEqual("LastName", (string)valueProvider.GetValue("Last").ConvertTo(typeof(string)));
            Assert.AreEqual(70.5M, (decimal)valueProvider.GetValue("Scores[0]").ConvertTo(typeof(decimal)));
            Assert.AreEqual(80.5M, (decimal)valueProvider.GetValue("Scores[1]").ConvertTo(typeof(decimal)));

            Assert.IsTrue(valueProvider.GetValue("Scores[2]").RawValue is int);

            // Last assertion means next line will throw an exception. Also means the default model binder won't take the value in an MVC app
            Assert.AreEqual(90M, (decimal)valueProvider.GetValue("Scores[2]").ConvertTo(typeof(decimal)));
        }

        [TestMethod]
        public void Deserialization_problem_explained()
        {
            // Arrange
            var student = new Student
                {
                    First = "FirstName",
                    Last = "LastName",
                    Scores = new[] {70.5M, 80.5M, 90M}
                };

            var json = new JavaScriptSerializer().Serialize(student);

            // Act
            var deserializedStudent = (IDictionary<string, object>)new JavaScriptSerializer().DeserializeObject(json);

            // Assert
            var scores = (IList)deserializedStudent["Scores"];
            Assert.IsInstanceOfType(scores[0], typeof(decimal));
            Assert.IsInstanceOfType(scores[1], typeof(decimal));
            Assert.IsInstanceOfType(scores[2], typeof(int));
        }

        private class Student
        {
            public string First { get; set; }
            public string Last { get; set; }
            public decimal[] Scores { get; set; }
        }
    }
}